{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 原文地址(https://mp.weixin.qq.com/s/\\_ur955AbBzYXVVyQl8H9VQ)\n",
    "\n",
    "### **01** **如何在运行状态查看源代码**\n",
    "\n",
    "查看函数的源代码，我们通常会使用 IDE 来完成。比如在 PyCharm 中，你可以 Ctrl + 鼠标点击 进入函数的源代码。\n",
    "\n",
    "那如果没有 IDE 呢？\n",
    "\n",
    "当我们想使用一个函数时，如何知道这个函数需要接收哪些参数呢？当我们在使用函数时出现问题的时候，如何通过阅读源代码来排查问题所在呢？\n",
    "\n",
    "这时候，我们可以使用 inspect 来代替 IDE 帮助你完成这些事\n",
    "\n",
    "运行结果如下\n",
    "\n",
    "### **02** **如何关闭异常自动关联上下文？**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "当你在处理异常时，由于处理不当或者其他问题，再次抛出另一个异常时，往外抛出的异常也会携带原始的异常信息。\n",
    "\n",
    "就像这样子。\n",
    "\n",
    "```py\n",
    "try:\n",
    "    print(1 / 0)\n",
    "except Exception as exc:\n",
    "    raise RuntimeError(\"Something bad happened\")\n",
    "\n",
    "```\n",
    "\n",
    "```\n",
    "从输出可以看到两个异常信息\n",
    "\n",
    "\n",
    "```\n",
    "\n",
    "如果在异常处理程序或 finally 块中引发异常，默认情况下，异常机制会隐式工作会将先前的异常附加为新异常的 \\_\\_context\\_\\_属性。这就是 Python 默认开启的自动关联异常上下文。\n",
    "\n",
    "如果你想自己控制这个上下文，可以加个 from 关键字（from 语法会有个限制，就是第二个表达式必须是另一个异常类或实例。），来表明你的新异常是直接由哪个异常引起的。\n",
    "\n",
    "```py\n",
    "try:\n",
    "    print(1 / 0)\n",
    "except Exception as exc:\n",
    "    raise RuntimeError(\"Something bad happened\") from exc\n",
    "\n",
    "```\n",
    "\n",
    "```\n",
    "输出如下\n",
    "\n",
    "\n",
    "```\n",
    "\n",
    "当然，你也可以通过 with\\_traceback() 方法为异常设置上下文\\_\\_context\\_\\_属性，这也能在 traceback 更好的显示异常信息。\n",
    "\n",
    "最后，如果我想彻底关闭这个自动关联异常上下文的机制？有什么办法呢？\n",
    "\n",
    "可以使用 raise...from None，从下面的例子上看，已经没有了原始异常\n",
    "\n",
    "```sh\n",
    "$ cat demo.py\n",
    "try:\n",
    "    print(1 / 0)\n",
    "except Exception as exc:\n",
    "    raise RuntimeError(\"Something bad happened\") from None\n",
    "$\n",
    "$ python demo.py\n",
    "Traceback (most recent call last):\n",
    "  File \"demo.py\", line 4, in <module>\n",
    "    raise RuntimeError(\"Something bad happened\") from None\n",
    "RuntimeError: Something bad happened\n",
    "(PythonCodingTime)\n",
    "\n",
    "```\n",
    "\n",
    "### **03** **最快查看包搜索路径的方式**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "当你使用 import 导入一个包或模块时，Python 会去一些目录下查找，而这些目录是有优先级顺序的，正常人会使用 sys.path 查看。\n",
    "\n",
    "那有没有更快的方式呢？\n",
    "\n",
    "我这有一种连 console 模式都不用进入的方法呢？\n",
    "\n",
    "你可能会想到这种，但这本质上与上面并无区别\n",
    "\n",
    "这里我要介绍的是比上面两种都方便的多的方法，一行命令即可解决。\n",
    "\n",
    "从输出你可以发现，这个列的路径会比 sys.path 更全，它包含了用户环境的目录。\n",
    "\n",
    "### **04** **将嵌套 for 循环写成单行**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "我们经常会如下这种嵌套的 for 循环代码\n",
    "\n",
    "这里仅仅是三个 for 循环，在实际编码中，有可能会有更层。\n",
    "\n",
    "这样的代码，可读性非常的差，很多人不想这么写，可又没有更好的写法。\n",
    "\n",
    "这里介绍一种我常用的写法，使用 itertools 这个库来实现更优雅易读的代码。\n",
    "\n",
    "输出如下\n",
    "\n",
    "### **05** **如何使用 print 输出日志**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "初学者喜欢使用 print 来调试代码，并记录程序运行过程。\n",
    "\n",
    "但是 print 只会将内容输出到终端上，不能持久化到日志文件中，并不利于问题的排查。\n",
    "\n",
    "如果你热衷于使用 print 来调试代码（虽然这并不是最佳做法），记录程序运行过程，那么下面介绍的这个 print 用法，可能会对你有用。\n",
    "\n",
    "Python 3 中的 print 作为一个函数，由于可以接收更多的参数，所以功能变为更加强大，指定一些参数可以将 print 的内容输出到日志文件中\n",
    "\n",
    "代码如下：\n",
    "\n",
    "### **06** **如何快速计算函数运行时间**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "计算一个函数的运行时间，你可能会这样子做\n",
    "\n",
    "你看看你为了计算函数运行时间，写了几行代码了。有没有一种方法可以更方便的计算这个运行时间呢？\n",
    "\n",
    "有。\n",
    "\n",
    "有一个内置模块叫 timeit，使用它，只用一行代码即可。\n",
    "\n",
    "运行结果如下\n",
    "\n",
    "### **0****7 利用自带的缓存机制提高效率**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "缓存是一种将定量数据加以保存，以备迎合后续获取需求的处理方式，旨在加快数据获取的速度。\n",
    "\n",
    "数据的生成过程可能需要经过计算，规整，远程获取等操作，如果是同一份数据需要多次使用，每次都重新生成会大大浪费时间。\n",
    "\n",
    "所以，如果将计算或者远程请求等操作获得的数据缓存下来，会加快后续的数据获取需求。\n",
    "\n",
    "为了实现这个需求，Python 3.2 + 中给我们提供了一个机制，可以很方便的实现，而不需要你去写这样的逻辑代码。\n",
    "\n",
    "这个机制实现于 functool 模块中的 lru\\_cache 装饰器。\n",
    "\n",
    "```\n",
    "@functools.lru\\_cache(maxsize=None, typed=False)\n",
    "```\n",
    "\n",
    "maxsize：最多可以缓存多少个此函数的调用结果，如果为 None，则无限制，设置为 2 的幂时，性能最佳。\n",
    "\n",
    "  \n",
    "\n",
    "typed：若为 True，则不同参数类型的调用将分别缓存。\n",
    "\n",
    "举个例子\n",
    "\n",
    "输出如下，可以看到第二次调用并没有真正的执行函数体，而是直接返回缓存里的结果\n",
    "\n",
    "第六点介绍的 timeit，现在可以用它来测试一下到底可以提高多少的效率。\n",
    "\n",
    "不使用 lru\\_cache 的情况下，运行时间 31 秒\n",
    "\n",
    "由于使用了 lru\\_cache 后，运行速度实在太快了，所以我将 n 值由 30 调到 500，可即使是这样，运行时间也才 0.0004 秒。提高速度非常显著。\n",
    "\n",
    "### **08 在程序退出前执行代码的技巧**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "使用 atexit 这个内置模块，可以很方便的注册退出函数。\n",
    "\n",
    "不管你在哪个地方导致程序崩溃，都会执行那些你注册过的函数。\n",
    "\n",
    "示例如下\n",
    "\n",
    "![](https://mmbiz.qpic.cn/mmbiz_png/QB6G4ZoE1854xOb24pEASdgfiaD0SstYjw08VCQgcsmhnLqvoxMayMyqs6hDibgGHJqNBqPU79u4ruZcTxGjlRUg/640?wx_fmt=png)\n",
    "\n",
    "如果 clean() 函数有参数，那么你可以不用装饰器，而是直接调用 atexit.register(clean\\_1, 参数 1, 参数 2, 参数 3='xxx')。\n",
    "\n",
    "可能你有其他方法可以处理这种需求，但肯定比上不使用 atexit 来得优雅，来得方便，并且它很容易扩展。\n",
    "\n",
    "但是使用 atexit 仍然有一些局限性，比如：\n",
    "\n",
    "如果程序是被你没有处理过的系统信号杀死的，那么注册的函数无法正常执行。\n",
    "\n",
    "如果发生了严重的 Python 内部错误，你注册的函数无法正常执行如果你手动调用了 os.\\_exit()，你注册的函数无法正常执行。\n",
    "\n",
    "### **09** **实现类似 defer 的延迟调用**\n",
    "\n",
    "  \n",
    "\n",
    "---\n",
    "\n",
    "在 Golang 中有一种延迟调用的机制，关键字是 defer，例如下面的示例\n",
    "\n",
    "输出如下，myfunc 的调用会在函数返回前一步完成，即使你将 myfunc 的调用写在函数的第一行，这就是延迟调用。\n",
    "\n",
    "```\n",
    "A\n",
    "B\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "```\n",
    "\n",
    "那么在 Python 中否有这种机制呢？\n",
    "\n",
    "当然也有，只不过并没有 Golang 这种简便。在 Python 可以使用 上下文管理器 达到这种效果\n",
    "\n",
    "输出如下\n",
    "\n",
    "### **10** **如何流式读取数 G 超大文件**\n",
    "\n",
    "使用 with...open... 可以从一个文件中读取数据，这是所有 Python 开发者都非常熟悉的操作。\n",
    "\n",
    "但是如果你使用不当，也会带来很大的麻烦。\n",
    "\n",
    "比如当你使用了 read 函数，其实 Python 会将文件的内容一次性的全部载入内存中，如果文件有 10 个 G 甚至更多，那么你的电脑就要消耗的内存非常巨大。\n",
    "\n",
    "对于这个问题，你也许会想到使用 readline 去做一个生成器来逐行返回。\n",
    "\n",
    "可如果这个文件内容就一行呢，一行就 10 个 G，其实你还是会一次性读取全部内容。\n",
    "\n",
    "最优雅的解决方法是，在使用 read 方法时，指定每次只读取固定大小的内容，比如下面的代码中，每次只读取 8kb 返回。\n",
    "\n",
    "上面的代码，功能上已经没有问题了，但是代码看起来代码还是有些臃肿。\n",
    "\n",
    "借助偏函数 和 iter 函数可以优化一下代码\n",
    "\n",
    "```py\n",
    "from functools import partial\n",
    "\n",
    "def read_from_file(filename, block_size = 1024 * 8):\n",
    "    with open(filename, \"r\") as fp:\n",
    "        for chunk in iter(partial(fp.read, block_size), \"\"):\n",
    "            yield chunk\n",
    "\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
